package org.dei.perla.utils.testframework;

import java.io.*;
import java.util.*;
import java.lang.reflect.*;
import org.dei.perla.parser.expressions.*;


/**
 * Classe che rappresenta una serie di SingleTest
 * @author Camplani, Fortunato, Marelli, Rota, Vigano' et al.
 */
public class TestExecutor {

	/**
	 * Percorso al file contenente gli elementi per i test
	 */
	private String pPathToFile;
	
	/**
	 * Oggetto Reader per leggere linea per linea
	 */
	private BufferedReader pBufferedReader;
	
	/**
	 * Costruttore dell'esecutore del test. 
	 * @param parPath Percorso del file
	 */
	public TestExecutor(String parPath) {
		
		this.pPathToFile = parPath;
		
		try {
			
			pBufferedReader = new BufferedReader(new FileReader(pPathToFile));
			System.out.println(".::Ok, file valido.");
			
		} catch (FileNotFoundException e) {
			
			System.out.println("ERRORE: File non valido!!");
			this.pBufferedReader = null;
		}	
		
	}
		
	/**
	 * Costruisce la classe Constant con il nome parConstantToCreate, inizializzandola con il valore in parValue
	 * nel caso parValue non sia significativo lasciare ""
	 */
	private Constant createConstant(String parConstantToCreate,String parValue) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
		
		Constructor<?> tConstantAsParamConstructor;
		Class<?> tConstantAsParamClass;
		Object tConstant;
		
		if (!parConstantToCreate.contains(".")) { //Per qualsiasi Constant che sia interna a PerLa
			
			parConstantToCreate = "org.dei.perla.parser.expressions."+parConstantToCreate;
			
		}
		
		tConstantAsParamClass = Class.forName(parConstantToCreate);			
		
		if (tConstantAsParamClass.getSuperclass()==ConstantUserDefined.class) {
		
			tConstantAsParamConstructor = tConstantAsParamClass.getConstructor(new Class[] {String.class,String.class});
			tConstant = tConstantAsParamConstructor.newInstance(parConstantToCreate,parValue);
		}
		
		else if (tConstantAsParamClass.equals(ConstantNull.class)) {
			
			tConstant = tConstantAsParamClass.newInstance();			
		}
		
		else if (tConstantAsParamClass.equals(ConstantTimestamp.class)) {
		
			tConstantAsParamConstructor = tConstantAsParamClass.getConstructor(new Class[] {long.class});
			Long tLong = new Long(parValue);
			tConstant = tConstantAsParamConstructor.newInstance(tLong.longValue());	
		}
		
		else { 
		
			tConstantAsParamConstructor = tConstantAsParamClass.getConstructor(new Class[] {String.class});
			tConstant = tConstantAsParamConstructor.newInstance(parValue);			
		}
		return (Constant)tConstant;
	}
	
	/**
	 * Costruisce un nodo (qualsiasi) ricevendo il nome e un arraylist delle costanti che racchiude.
	 * @param parNodeToCreate String con il nome della classe del nodo da creare
	 * @param parArrayListConstant ArrayList con le costanti che il nodo wrappa.
	 */
	private Node createNode(String parNodeToCreate,ArrayList<Constant> parArrayListConstant) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
		
		Class<?> tNodeClass;
		Constructor<?> tNodeConstructor = null;
		ArrayList<NodeConstant> tNodes =  new ArrayList<NodeConstant>();
		Class<?>[] tTemp;
		Object NodeToBeReturned;

		if (parArrayListConstant == null) { //Se è un Exception non viene costruita.
			
			return null;
			}
		
		tTemp = new Class[parArrayListConstant.size()]; 
			
		if (parNodeToCreate.equals("NodeConstant")) { //Il nodeConstant ha "constant" come parametri!
						
			tTemp[0]= Constant.class;
	
			} 
		else { //gli altri nodi hanno 1/2/3 NodeConstant come parametri
			
			for (int i=0;i<tTemp.length;i++ ) {tTemp[i] = Node.class;}
			
		}		
		
		tNodeClass = Class.forName("org.dei.perla.parser.expressions."+parNodeToCreate);
		tNodeConstructor = tNodeClass.getConstructor(tTemp);
			
		for (Constant tConst : parArrayListConstant) {
				
			tNodes.add(new NodeConstant(tConst));				
		}
			
		if (tTemp[0] == Constant.class) {
		   
			NodeToBeReturned = tNodeConstructor.newInstance(parArrayListConstant.get(0));}
		
		else {
			
			NodeToBeReturned = tNodeConstructor.newInstance(tNodes.toArray());}
		 
	return (Node)NodeToBeReturned;
	}
	
	/**
	 * Parsa la linea attraverso le regex e costruisce il single test appropriato
	 */
	private SingleTest lineParser(String parLine) throws SecurityException, IllegalArgumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
		
		LineParser tLineParser = new LineParser();
		String[] tLineParsed = tLineParser.parse(parLine);
		//String[] tLineParsed = parLine.split("( |\t)+");
		Node tNodeToTest,tNodeCorrect = null;
		ArrayList<Constant> tConstantArrayList = new ArrayList<Constant>();
		ArrayList<Class<?>> tHopedResultsArrayList = new ArrayList<Class<?>>();
		ArrayList<Constant> tTemp = null;
		
		if (tLineParsed[0].equals("N")) {
			
			for (int i=2;i<(Array.getLength(tLineParsed))-3;i=i+2) {
							
				Constant tConstant = createConstant(tLineParsed[i],tLineParsed[i+1]);
				tConstantArrayList.add(tConstant);
								
			}
			
			tNodeToTest = createNode(tLineParsed[1],tConstantArrayList);
			
			//devo costuire l'arraylist deI risultati attesi
						
			Class<?> tResultClass = Class.forName("org.dei.perla.parser.expressions."+tLineParsed[(Array.getLength(tLineParsed))-2]);
			Class<?> tResultTypeClass = Class.forName("org.dei.perla.parser.expressions."+tLineParsed[Array.getLength(tLineParsed)-3]); 
			tHopedResultsArrayList.add(tResultTypeClass);
			tHopedResultsArrayList.add(tResultClass);
		
			if (!tLineParsed[(Array.getLength(tLineParsed))-2].contains("Exception")) {
			
				tTemp = new ArrayList<Constant>();
				tTemp.add(createConstant(tLineParsed[(Array.getLength(tLineParsed))-2],tLineParsed[(Array.getLength(tLineParsed))-1]));
			
			}
			
			tNodeCorrect= createNode("NodeConstant",tTemp);
			
			return new SingleTest(tNodeToTest,tHopedResultsArrayList,tNodeCorrect);
			
		}
		
		else {
		return null;
		}
	}

	/**
	 * Crea la classe SingleTest a seconda della linea che è stata parsata e lo esegue
	 */
	public void runTest() {
		
		String tString;
				
		if (this.pBufferedReader!=null) {
		
			try {
			
				while ((tString=pBufferedReader.readLine())!=null) {
				System.out.println("\nLINEA: "+tString.replaceAll("( |\t)+", " "));
				SingleTest tTestToBeExecuted = this.lineParser(tString);	
				tTestToBeExecuted.test();	
				
				}
			
			}		
			catch (Exception e) {
				
				System.out.println("\n\nERRORE di parsing della linea, ricontrollare");
				e.printStackTrace();
				
			};
			
		} else {System.out.println("Ricontrollare path");}
		
		System.out.println("\n.::Test concluso");
		
		
	}
		
}
	

	
	
	
	
	

	
